package com.changgou.oauth.service.impl;

import com.changgou.oauth.service.AuthService;
import com.changgou.oauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class AuthServiceImpl implements AuthService {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${auth.ttl}")
    private long ttl;

    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {

        //1.申请令牌
        /*
            构建请求地址  http://localhost:9200/oauth/token
         */
        //采用客户端负载均衡，从eureka获取认证服务的ip 和端口
        ServiceInstance serviceInstance = loadBalancerClient.choose("USER-AUTH");
        URI uri = serviceInstance.getUri();
        //申请令牌地址
        String url = uri + "/oauth/token";

        /*
            封装请求参数 body  headers
        */
        //指定认证类型、账号、密码
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type","password");
        body.add("username","itheima");
        body.add("password","itheima");
        //进行Base64编码,并将编码后的认证数据放到头文件中
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add("Authorization", this.getHttpBasic("changgou","changgou"));

        /*
        当后端出现了400,401(没有权限),后端不对这两个异常编码进行处理,而是直接返回给前端
         */
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            //会对后端所有的异常进行处理
            public void handleError(ClientHttpResponse response) throws IOException {
                if(response.getRawStatusCode()!=400&&response.getRawStatusCode()!=401){}
                super.handleError(response);
            }
        });

        HttpEntity<MultiValueMap<String,String>> requestEntity = new HttpEntity<>(body,headers);
        //发送请求
        /**
         * 第一个参数,被请求的地址信息
         * 第二个参数,当前的请求方式
         * 第三个参数,用来封装我们当前的请求相关参数
         * 第四个参数,当前数据的返回类型
         */
        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, Map.class);
        Map map = responseEntity.getBody();
        if (map==null || map.get("access_token")==null || map.get("refresh_token")==null || map.get("jti")==null){
            throw new RuntimeException("申请令牌失败");
        }

        //2.封装数据结果
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken((String) map.get("access_token"));     //jwt的值
        authToken.setRefreshToken((String) map.get("refresh_token"));   //刷新令牌的值
        authToken.setJti((String) map.get("jti"));                      //jti的值

        //3.将jti作为redis中的key,将jwt作为redis中的value进行数据的存放
        stringRedisTemplate.boundValueOps(authToken.getJti()).set(authToken.getAccessToken(),ttl, TimeUnit.SECONDS);


        return authToken;
    }

    /***
     * base64编码
     * @param clientId
     * @param clientSecret
     * @return
     */
    private String getHttpBasic(String clientId, String clientSecret) {
        //将客户端id和客户端密码拼接，按“客户端id:客户端密码”
        String value = clientId+":"+clientSecret;
        //进行base64编码
        byte[] encode = Base64Utils.encode(value.getBytes());
        //Basic Y2hhbmdnb3U6Y2hhbmdnb3U=
        return "Basic "+new String(encode);
    }
}
